package main

import "fmt"

/*
Go语言中的多态性是在接口的帮助下实现的——定义接口类型，创建实现该接口的结构体对象
	TIPS：
		1. Go语言中没有多态，这里是指多态性；
		2. 接口类型的对象，不能访问其实现类中的属性字段
*/
func main() {

	/*
		一. 接口实现多态性
	*/
	billing := FixedBilling{
		name:   "1",
		amount: 1000,
	}
	advert := Advert{
		adName:     "2",
		clickCount: 100,
		income:     100,
	}

	project := Project{
		name:   "3",
		works:  100,
		hourly: 1000,
	}
	// TIPS：只有隐式地实现了Income接口，才能放入同一个指定类型的切片中。假设以下三个实例中对应的结构体有任意一个没有全部实现
	//		 Income接口的所有方法，这里将提示报错
	incomes := []Income{billing, advert, project}
	// 尽管添加了新的收入方式，但没有对calculateNetIncome()函数做任何更改，全靠多态性起作用
	fmt.Println(totalIncome(incomes))

	/*
		二. 空接口。空接口中没有任何方法，任意类型都可以实现该接口。空接口常用于以下情形：
			• println的参数就是空接口；
			• 定义一个map：key是string，value是任意数据类型；
			• 定义一个切片，其中存储任意类型的数据
	*/
	// 任意类型都可以实现该接口
	var a1 A = FixedBilling{
		name:   "FixedBilling",
		amount: 0,
	}
	var a2 A = Project{
		name:   "Project",
		works:  0,
		hourly: 0,
	}
	var a3 A = Advert{
		adName:     "Advert",
		clickCount: 0,
		income:     0,
	}
	var a4 A = 100
	var a5 A = "空接口"
	fmt.Println(a1)
	fmt.Println(a2)
	fmt.Println(a3)
	fmt.Println(a4)
	fmt.Println(a5)
	// fmt.println 参数就是空接口
	fmt.Println("fmt.println 参数就是空接口，可以接受任何数据类型", 100, "sss", 3.1415, a1)
	// 定义map，value是任何数据类型
	m := make(map[string]interface{})
	m["name"] = "string类型"
	m["age"] = 13
	m["heigh"] = 176.78
	m["advert"] = advert
	fmt.Println(m)
	// 定义切片，其中存储任一数据类型
	i := make([]interface{}, 0, 10)
	i = append(i, a1, a2, a3, a4, a5)
	fmt.Println(i)
	// 	TIPS：当参数的可变参数是空接口类型时，传入空接口的切片时需要注意参数展开的问题
	i2 := []interface{}{1, 2, 4, "43434", 89.3, false}
	fmt.Println(i2)
	fmt.Println(i2...)
	InterfaceArg(i2)
	InterfaceArg2(i2...)
}

/*
空接口
*/
type A interface {
}

func InterfaceArg(i2 []interface{}) {

	for _, i3 := range i2 {
		fmt.Println(i3)
	}
}
func InterfaceArg2(i3 ...interface{}) {

	for _, i := range i3 {
		fmt.Println(i)
	}
}

type Cal2 interface {

	// 计算收入金额
	cal() float64
}

type Source interface {

	// 说明收入来源
	source() string
}

/*
接口组合
*/
type Income interface {
	Cal2
	Source
}

type FixedBilling struct {

	// 项目名
	name string
	// 招标金额
	amount float64
}

type Project struct {

	// 生产项目
	name string
	// 工作时长
	works float64
	// 工资率
	hourly float64
}

type Advert struct {

	// 广告名
	adName string
	// 点击量
	clickCount int
	// 收入
	income float64
}

func (f FixedBilling) cal() float64 {

	return f.amount
}

func (f FixedBilling) source() string {

	return f.name
}

func (p Project) cal() float64 {

	return p.hourly * p.works
}

func (p Project) source() string {

	return p.name
}

func (a Advert) cal() float64 {

	return float64(a.clickCount) * a.income
}

func (a Advert) source() string {

	return a.adName
}

func totalIncome(total []Income) float64 {

	incomes := 0.0

	for _, income := range total {

		fmt.Printf("收入来源：%s, 收入金额：%.2f \n", income.source(), income.cal())
		incomes += income.cal()
	}

	return incomes
}
